package net.xdclass.coupon.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.common.JsonData;
import net.xdclass.common.PageJsonData;
import net.xdclass.common.constant.Cons;
import net.xdclass.common.enums.BizCodeEnum;
import net.xdclass.common.enums.ProductOrderStateEnum;
import net.xdclass.common.exception.BizException;
import net.xdclass.common.message.MessageInfo;
import net.xdclass.common.model.LoginUser;
import net.xdclass.coupon.enums.CouponUseStatus;
import net.xdclass.common.enums.StockTaskStateEnum;
import net.xdclass.coupon.feign.OrderFeignClient;
import net.xdclass.coupon.mapper.CouponRecordMapper;
import net.xdclass.coupon.mapper.CouponTaskMapper;
import net.xdclass.coupon.message.CouponMessageProducer;
import net.xdclass.coupon.message.CouponRecordMessageInfo;
import net.xdclass.coupon.model.CouponRecordDO;
import net.xdclass.coupon.model.CouponTaskDO;
import net.xdclass.coupon.request.LockCouponRecordRequest;
import net.xdclass.coupon.service.CouponRecordService;
import net.xdclass.coupon.vo.CouponRecordVO;
import net.xdclass.local.ThreadLocal;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CouponRecordServiceImpl implements CouponRecordService {

    @Autowired
    private CouponRecordMapper couponRecordMapper;
    @Autowired
    private CouponTaskMapper couponTaskMapper;
    @Autowired
    private CouponMessageProducer couponMessageProducer;
    @Autowired
    private OrderFeignClient orderFeignClient;

    /**
     * 锁定优惠券库存
     *
     * @param request
     * @return
     */
    @Transactional
    @Override
    public JsonData lockCouponRecord(LockCouponRecordRequest request) {
        LoginUser loginUser = ThreadLocal.getLoginUser();
        String orderOutTradeNo = request.getOrderOutTradeNo();
        List<Long> lockCouponRecordIds = request.getLockCouponRecordIds();
        if (CollectionUtils.isEmpty(lockCouponRecordIds)){
            return JsonData.buildSuccess();
        }

        int updateLine = couponRecordMapper.lockUseStateBatch(loginUser.getId(), CouponUseStatus.USED.name(), lockCouponRecordIds);
        log.info("优惠券更新行【{}】", updateLine);
        List<CouponTaskDO> couponTaskList = lockCouponRecordIds.stream().map(id -> {
            CouponTaskDO task = new CouponTaskDO();
            task.setCouponRecordId(id);
            task.setCreateTime(new Date());
            task.setOutTradeNo(orderOutTradeNo);
            task.setLockState(StockTaskStateEnum.LOCK.name());
            return task;
        }).collect(Collectors.toList());
        int insertLine = couponTaskMapper.insertBatch(couponTaskList);
        log.info("优惠券锁定行【{}】", insertLine);
        if (updateLine != lockCouponRecordIds.size() || updateLine != insertLine){
            throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
        }
        for(CouponTaskDO couponTaskDO : couponTaskList){
            CouponRecordMessageInfo couponRecordMessage = new CouponRecordMessageInfo();
            couponRecordMessage.setOutTradeNo(orderOutTradeNo);
            couponRecordMessage.setTaskId(couponTaskDO.getId());
            couponRecordMessage.setTime(new Date());

            couponMessageProducer.sendMsg(couponRecordMessage);
            log.info("优惠券锁定消息发送成功:{}", couponRecordMessage);
        }
        return JsonData.buildSuccess();
    }

    /**
     * 释放优惠券库存锁定
     *
     * @param messageInfo
     * @return
     */
    @Transactional
    @Override
    public boolean releaseCouponRecord(MessageInfo messageInfo) {
        if (!(messageInfo instanceof CouponRecordMessageInfo)){
            return true;
        }
        CouponRecordMessageInfo couponRecord = (CouponRecordMessageInfo) messageInfo;
        // 查询task是否存在
        LambdaQueryWrapper<CouponTaskDO> wrapper = Wrappers.<CouponTaskDO>lambdaQuery().eq(CouponTaskDO::getId, couponRecord.getTaskId());
        CouponTaskDO task = couponTaskMapper.selectOne(wrapper);
        if (task == null){
            log.info("优惠券锁定任务不存在【{}】", couponRecord);
            return true;
        }
        if (StockTaskStateEnum.LOCK.name().equalsIgnoreCase(task.getLockState())){
            // LOCK状态，需要关单
            // 1、查询订单状态
            JsonData data = orderFeignClient.queryOrderState(couponRecord.getOutTradeNo());
            if (data != null && Cons.SUCCESS.equals(data.getCode())){
                String state = data.getData().toString();
                if (ProductOrderStateEnum.NEW.name().equalsIgnoreCase(state)){
                    log.warn("订单尚未支付【{}】", couponRecord.getOutTradeNo());
                    return false;
                }
                if (ProductOrderStateEnum.PAY.name().equalsIgnoreCase(state)){
                    // 更新task状态 -> FINISH
                    task.setLockState(StockTaskStateEnum.FINISH.name());
                    couponTaskMapper.updateById(task);
                    return true;
                }
            }
            // 更新优惠券状态为NEW
            log.warn("订单不存在或已取消。释放优惠券。【{}】", task);
            CouponRecordDO couponRecordDO = new CouponRecordDO();
            couponRecordDO.setId(task.getCouponRecordId());
            couponRecordDO.setUseState(CouponUseStatus.NEW);
            couponRecordMapper.updateById(couponRecordDO);
            task.setLockState(StockTaskStateEnum.CANCEL.name());
            couponTaskMapper.updateById(task);
            return true;
        }else {
            log.info("任务状态非LOCK，【{}】", task);
            return true;
        }
    }

    /**
     * 根据id查询优惠券记录
     *
     * @param recordId
     * @return
     */
    @Override
    public CouponRecordVO selectById(Long recordId) {
        LoginUser loginUser = ThreadLocal.getLoginUser();
        QueryWrapper<CouponRecordDO> wrapper = new QueryWrapper<>();
        wrapper.eq("id", recordId).eq("user_id", loginUser.getId());
        CouponRecordDO couponRecordDO = couponRecordMapper.selectOne(wrapper);
        if (couponRecordDO != null){
            return toVo(couponRecordDO);
        }
        return null;
    }

    /**
     * 分页查询优惠券列表
     *
     * @param page
     * @param size
     * @return
     */
    @Override
    public JsonData page(int page, int size) {
        LoginUser loginUser = ThreadLocal.getLoginUser();
        IPage<CouponRecordDO> p = new Page<>(page, size);
        QueryWrapper<CouponRecordDO> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", loginUser.getId())
                .orderByDesc("start_time");
        IPage<CouponRecordDO> couponRecordDOIPage = couponRecordMapper.selectPage(p, wrapper);
        if (couponRecordDOIPage != null && !CollectionUtils.isEmpty(couponRecordDOIPage.getRecords())){
            List<CouponRecordVO> vos = couponRecordDOIPage.getRecords().stream().map(this::toVo).collect(Collectors.toList());
            PageJsonData pageJsonData = new PageJsonData(page, size, (int) p.getTotal());
            pageJsonData.setData(vos);
            return pageJsonData;
        }
        return JsonData.buildSuccess();
    }

    private CouponRecordVO toVo(CouponRecordDO couponRecordDO){
        CouponRecordVO vo = new CouponRecordVO();
        if (couponRecordDO == null){
            return null;
        }
        BeanUtils.copyProperties(couponRecordDO, vo);
        return vo;
    }
}
